package com.richinfo.buddy.common.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.richinfo.buddy.common.enums.OrderDirEnum;
import com.richinfo.buddy.common.model.QueryExample;
import com.richinfo.buddy.common.model.QueryOrder;
import com.richinfo.buddy.common.utils.SpringBeanUtils;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

public abstract class BaseServicesImpl<T, ID> implements BaseServices<T, ID> {

    private SqlSessionFactory sqlSessionFactory;

    private TransactionTemplate transactionTemplate;

    private Map<String, String> baseResultMap;

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    public abstract Mapper<T> getMapper();

    @PostConstruct
    protected void postConstruct() {
        sqlSessionFactory = getOrNull(sqlSessionFactoryName());
        PlatformTransactionManager transactionManager = getOrNull(transactionManagerName(), true);
        transactionTemplate = new TransactionTemplate(transactionManager);
    }

    protected String sqlSessionFactoryName() {
        return "sqlSessionFactory";
    }

    protected String transactionManagerName() {
        return "transactionManager";
    }

    private <T> T getOrNull(String beanName) {
        return getOrNull(beanName, false);
    }

    private <T> T getOrNull(String beanName, boolean required) {
        try {
            return SpringBeanUtils.getBean(beanName);
        } catch (BeansException e) {
            if (required) {
                throw e;
            } else {
                logger.warn("没有找到bean name:[{}]", beanName);
            }
        }
        return null;
    }

    @Override
    public Integer add(T t) {
        return transactionTemplate.execute(status -> getMapper().insertSelective(t));
    }

    @Override
    public Integer deleteById(ID id) {
        return transactionTemplate.execute(status -> getMapper().deleteByPrimaryKey(id));
    }

    @Override
    public Integer updateByPrimaryKey(T t) {
        return transactionTemplate.execute(status -> getMapper().updateByPrimaryKeySelective(t));
    }

    @Override
    public List<T> findAll() {
        return getMapper().selectAll();
    }

    @Override
    public T findOne(T t) {
        if (t != null) {
            return getMapper().selectOne(t);
        }
        return null;
    }

    @Override
    public Integer delete(T t) {
        if (t != null) {
            return transactionTemplate.execute(status -> getMapper().delete(t));
        }
        return 0;
    }

    @Override
    public T findById(ID id) {
        if (id == null) {
            return null;
        }
        return getMapper().selectByPrimaryKey(id);
    }

    @Override
    public Integer batchAdd(List<T> list) {
        Assert.notNull(list, "list can not be null!");
        return transactionTemplate.execute(status -> {
            int i = 0;
            for (T t : list) {
                i += getMapper().insertSelective(t);
            }
            return i;
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer batchUpdateByPrimaryKey(List<T> list) {
        Assert.notNull(list, "list can not be null!");
        return transactionTemplate.execute(status -> {
            int i = 0;
            for (T t : list) {
                i += getMapper().updateByPrimaryKeySelective(t);
            }
            return i;
        });
    }

    protected Map<String, String> getBaseResultMap() {
        if (baseResultMap == null) {
            baseResultMap = getResultMap("BaseResultMap");
        }
        return baseResultMap;
    }

    protected Map<String, String> getResultMap(Class<?> mapperClass, String resultMapId) {
        return getResultMap(mapperClass.getTypeName(), resultMapId);
    }

    protected Map<String, String> getResultMap(String resultMapId) {
        return getResultMap(getMapper().getClass().getGenericInterfaces()[0].getTypeName(), resultMapId);
    }

    protected Map<String, String> getResultMap(String mapperClassName, String resultMapId) {
        Map<String, String> resultMap = new HashMap<String, String>();
        ResultMap map = sqlSessionFactory.getConfiguration().getResultMap(mapperClassName + "." + resultMapId);
        for (ResultMapping mapping : map.getPropertyResultMappings()) {
            resultMap.put(mapping.getProperty(), mapping.getColumn());
        }
        return resultMap;
    }

    protected void mappingSortColumns(List<QueryOrder> orderList) {
        if (orderList != null && orderList.size() > 0) {
            QueryOrder o = null;
            for (ListIterator<QueryOrder> it = orderList.listIterator(); it.hasNext(); ) {
                o = it.next();
                if (o == null) {
                    continue;
                }
                o.setOrderColumn(getBaseResultMap().get(o.getOrderColumn()));
                if (StringUtils.isNotBlank(o.getOrderColumn())) {
                    if (o.getOrderDir() == null) {
                        o.setOrderDir(OrderDirEnum.DESC);
                    }
                } else {
                    it.remove();
                }
            }
        }
    }

    @Override
    public List<T> find(T t) {
        return getMapper().select(t);
    }

    @Override
    public Integer updateByExampleSelective(QueryExample<T> queryExample) {
        return transactionTemplate.execute(status -> getMapper().updateByExampleSelective(queryExample.getRecord(), queryExample.getExample()));
    }

    @Override
    public PageInfo<T> findPage(T t, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return new PageInfo<>(this.find(t));
    }

    @Override
    public List<T> findByExample(Example example) {
        return getMapper().selectByExample(example);
    }

    @Override
    public Integer updateByExampleSelective(T record, Example query) {
        return transactionTemplate.execute(status -> getMapper().updateByExampleSelective(record, query));
    }

    @Override
    public Integer updateByExampleSelective(T record, T query) {
        Example example = new Example(query.getClass());
        example.and().andEqualTo(query);
        return transactionTemplate.execute(status -> getMapper().updateByExampleSelective(record, example));
    }

    @Override
    public Integer findCount(T record) {
        return getMapper().selectCount(record);
    }

    @Override
    public Integer findCountByExample(Example query) {
        return getMapper().selectCountByExample(query);
    }
}
